07 Output property

Próxima Atividade
  • Sugerir melhoria
Play
Current Time 0:00
/
Duration Time 0:00
Remaining Time -0:00
Stream TypeLIVE
Loaded: 0%
Progress: 0%
0:00
Fullscreen
00:00
Mute
Playback Rate
  • 2x
  • 1.5x
  • 1.2x
  • 1x
  • 0.8x
  • 0.7x
1x
Subtitles
  • subtitles off
Captions
  • captions off
Chapters
  • Chapters
HD

Transcrição

É importante entendermos que, à medida em que digitamos no campo de busca no navegador, o evento keyup está sendo disparado. Da mesma forma, o debounce é executado, porém não há nada inscrito nele. Antes, realizávamos o subscribe() passando o termo buscado à propriedade filter de PhotoListComponent, para filtrarmos a lista.

Entretanto, desta vez estamos em um componente, e o valor filter precisa chegar a outro. O ngOnInit() de search.component.ts ficará assim:

ngOnInit(): void {
    this.debounce
    .pipe(debounceTime(300))
    .subscribe(filter => console.log(filter));
}

No navegador, conforme vamos digitando o termo que queremos buscar e consultamos o console, confirmaremos que os dados estão sendo armazenados corretamente. Precisamos fazer com que o valor de console, neste componente específico, de alguma forma caia em filter de PhotoListComponent.

Já aprendemos a trabalhar com a inbound property, então, na tag <ap-search> de photo-list.component.html, teremos um evento customizado ("custom event") chamado onTyping. Trata-se de um evento do componente que nós definiremos da maneira que preferirmos. Sabemos que para fazermos um Data binding de eventos é necessário colocarmos a propriedade entre parênteses, então indicaremos que neste <ap-search> que irá disparar o evento onTyping, filter receberá $event.

Com isso, informamos que, conforme vamos digitando no campo de busca, o componente irá disparar este evento cujo $event será o input digitado, o qual será levado a filter de PhotoListComponent. Para criarmos um evento customizado, em search.component.ts adicionaremos um @Output() chamado onTyping, mesmo nome que colocamos no evento. Além disso, precisamos indicar que @Output() é um new EventEmitter, tomando cuidado para que isto não seja importado do pacote errado — neste caso, queremos que seja do próprio Angular.

Podemos definir um tipo genérico em EventEmitter(), e como usaremos um valor que é um texto, o evento emitirá um valor de tipo string.

export class SearchComponent implements OnInit, OnDestroy {

@Output() onTyping = new EventEmitter<string>();
debounce: Subject<string> = new Subject<string>();

// código omitido
}

O TypeScript automaticamente infere o tipo, porém é possível enfatizar isso usando dois pontos seguido de EventEmitter<string>, o que não faremos neste caso.

Salvaremos as alterações e, no subscribe() de ngOnInit(), trocaremos console.log(filter) por this.onTyping.emit(filter), pois toda vez que passarmos pelo debounce o filter será gerado:

ngOnInit(): void {
    this.debounce
    .pipe(debounceTime(300))
    .subscribe(filter => this.onTyping.emit(filter));
}

Com isso, a instrução filter => this.onTyping.emit(filter) só será pausada quando atingirmos 300ms sem digitarmos nada no campo de busca. Daí, será pego o valor digitado (filter), e o evento onTyping será emitido com o mesmo valor. Em photo-list.component.html, o $event (que nós programamos) é o valor que passamos pelo filter. Vamos salvar tudo e conferir seu funcionamento no navegador.

Desta vez, o filtro funciona corretamente! Aprendemos a lidar com a comunicação entre um elemento filho e um elemento pai. E podemos trabalhar com variações. Porém, há algo que pode confundir o usuário: após a busca e filtragem de imagens que correspondem a "farol", por exemplo, o clique no botão "Load more" não acarretará em nada.

Isso porque o filtro está em ação, e o usuário não saberá se as imagens estão sendo carregadas com sucesso, ou não. Podemos convencionar que, ao clique de "Load more", o filtro deverá ser limpo e as imagens serão recarregadas. Sendo assim, voltaremos a photo-list.component.ts e, em load(), com a chegada de novos dados, indicaremos que filter recebe uma string em branco:

load() {
    this.photoService
        .listFromUserPaginated(this.userName, ++this.currentPage)
        .subscribe(photos => {
            this.filter = '';
            this.photos = this.photos.concat(photos);
            if(!photos.length) this.hasMore = false;
        });
}

E então retornaremos ao navegador e repetiremos a ação de digitarmos "farol" e clicarmos em "Load more". Isto fará com que as imagens sejam recarregadas e o filtro seja limpado, porém o componente de busca não está ciente disso, portanto não é atualizado. Uma das formas de resolvermos isso é incluir @Input() com valor de tipo string:

@Output() onTyping = new EventEmitter<string>();
@Input() value: string = '';
debounce: Subject<string> = new Subject<string>();

Em photo-list.component.html, faremos o Data binding com filter para que ele seja limpo:

<ap-search
    (onTyping)="filter = $event" [value]="filter">
</ap-search>

E em search.component.html, incluiremos a linha [value]="value" dentro da tag <input>. Salvaremos tudo, recarregaremos a página no navegador. Atingimos nosso objetivo, e esta é uma das maneiras de conseguirmos lidar com a comunicação entre componentes que possuem alguma hierarquia entre si.

Tirar dúvida Próxima Atividade
ícone Angular parte 1: Fundamentos

Angular parte 1: Fundamentos

100%
Aula07 de 07

Lapidando ainda mais nossa aplicação

Atividades 7 de 14

  • 01 Projeto da aula anterior
  • 02 Submódulos 14min
  • 03 Integração com Font Awesome 09min
  • 04 Component container e ng-content 14min
  • 05 Desaparecimento de elementos
  • 06 Componentizando o filtro 08min
  • 07 Output property 09min
  • 08 Sobre output property
  • 09 Criando a primeira diretiva 06min
  • 10 Terminando a implementação da diretiva 11min
  • 11 Sobre diretivas
  • 12 Considerações finais 01min
  • 13 Consolidando seu conhecimento
  • 14 Projeto completo

Aulas

Outros Links

Fórum Trocar Curso
Gravatar de Marcos Katsumi Kay

Marcos Katsumi Kay 34.4k xp

Logo da Alura